<?php

namespace Zeke\Net\Http\Stream;


use Zeke\Zee\Event\EventEmitter;
use Zeke\Contracts\Io\Stream\ReadableStreamContracts;
use Zeke\Contracts\Io\Stream\WritableStreamContracts;
use Zeke\Zee\Util;

/**
 * [Internal] Pauses a given stream and buffers all events while paused
 *
 * This class is used to buffer all events that happen on a given stream while
 * it is paused. This allows you to pause a stream and no longer watch for any
 * of its events. Once the stream is resumed, all buffered events will be
 * emitted. Explicitly closing the resulting stream clears all buffers.
 *
 * 该类用于缓冲在暂停时给定流上发生的所有事件。这允许您暂停流，不再关注它的任何事件。
 * 一旦恢复流，所有缓冲的事件将被发射。显式关闭生成的流清除所有缓冲区。
 *
 * Note that this is an internal class only and nothing you should usually care
 * about.
 *
 * @see ReadableStreamContracts
 * @internal
 */
class PauseBufferStream extends EventEmitter implements ReadableStreamContracts
{
    private $input;
    private $closed = false;
    private $paused = false;
    private $dataPaused = '';
    private $endPaused = false;
    private $closePaused = false;
    private $errorPaused;
    private $implicit = false;

    public function __construct(ReadableStreamContracts $input)
    {
        $this->input = $input;

        $this->input->on('data', array($this, 'handleData'));
        $this->input->on('end', array($this, 'handleEnd'));
        $this->input->on('error', array($this, 'handleError'));
        $this->input->on('close', array($this, 'handleClose'));
    }

    /**
     * pause and remember this was not explicitly from user control
     * 继续接收子stream发送的数据，并将数据缓冲，不触发data事件
     */
    public function pauseImplicit()
    {
        $this->pause();
        $this->implicit = true;
    }

    /**
     * resume only if this was previously paused implicitly and not explicitly from user control
     * 恢复data事件，如果此时已缓冲了body数据，则立即触发data事件
     * @internal
     */
    public function resumeImplicit()
    {
        if ($this->implicit) {
            $this->resume();
        }
    }


    /**
     * 是否可读
     *
     * @return bool
     *
     * @author <liu.bin@ruaby.com> 2018-08-31 11:25
     */
    public function isReadable()
    {
        return !$this->closed;
    }


    /**
     * 暂停
     *
     * @author <liu.bin@ruaby.com> 2018-08-31 11:25
     */
    public function pause()
    {
        if ($this->closed) {
            return;
        }

        $this->input->pause();
        $this->paused = true;
        $this->implicit = false;
    }


    /**
     * 恢复，如果已缓冲了body数据，则立即触发data事件
     *
     * @author <liu.bin@ruaby.com> 2018-08-31 11:25
     */
    public function resume()
    {
        if ($this->closed) {
            return true;
        }

        $this->paused = false;
        $this->implicit = false;

        if ($this->dataPaused !== '') {
            $this->emit('data', array($this->dataPaused));
            $this->dataPaused = '';
        }

        if ($this->errorPaused) {
            $this->emit('error', array($this->errorPaused));
            return $this->close();
        }

        if ($this->endPaused) {
            $this->endPaused = false;
            $this->emit('end');
            return $this->close();
        }

        if ($this->closePaused) {
            $this->closePaused = false;
            return $this->close();
        }

        $this->input->resume();
    }


    /**
     *
     *
     * @param WritableStreamContracts $dest
     * @param array $options
     * @return WritableStreamContracts
     *
     *
     * @author <liu.bin@ruaby.com> 2018-08-31 11:31
     */
    public function pipe(WritableStreamContracts $dest, array $options = array())
    {
        Util::pipe($this, $dest, $options);

        return $dest;
    }


    /**
     * 关闭
     *
     *
     *
     * @author <liu.bin@ruaby.com> 2018-08-31 11:31
     */
    public function close()
    {
        if ($this->closed) {
            return;
        }

        $this->closed = true;
        $this->dataPaused = '';
        $this->endPaused = $this->closePaused = false;
        $this->errorPaused = null;

        $this->input->close();

        $this->emit('close');
        $this->removeAllListeners();
    }


    /**
     * 子stream触发data事件后执行的处理函数
     * 如果是暂停状态，则会把数据缓冲下来，不去触发data事件，通知上层stream
     *
     * @param $data
     *
     * @author <liu.bin@ruaby.com> 2018-08-31 11:31
     */
    public function handleData($data)
    {
        if ($this->paused) {
            $this->dataPaused .= $data;
            return;
        }

        $this->emit('data', array($data));
    }

    /** @internal */
    public function handleError(\Exception $e)
    {
        if ($this->paused) {
            $this->errorPaused = $e;
            return;
        }

        $this->emit('error', array($e));
        $this->close();
    }

    /** @internal */
    public function handleEnd()
    {
        if ($this->paused) {
            $this->endPaused = true;
            return;
        }

        if (!$this->closed) {
            $this->emit('end');
            $this->close();
        }
    }

    /** @internal */
    public function handleClose()
    {
        if ($this->paused) {
            $this->closePaused = true;
            return;
        }

        $this->close();
    }


}
